#!/usr/bin/env ruby

def is_upper c
    c >= 65 && c <= 90
end

def is_lower c
    c >= 97 && c <= 122
end

def is_punct c
    [32, 33, 34, 39, 46, 63].include? c
end

def valid_string s, lax
    if lax
        return s =~ /^[[:print:]]+$/
    else
        s.length.times do |i|
            return false unless (is_upper(s[i]) || is_lower(s[i]) || is_punct(s[i]))
        end
    end
    true
end

def usage
    $stderr.puts "USAGE: #{File.basename($0)} <index> <prefix> <ct1> <ct2>"
    $stderr.puts "-"*40
    $stderr.puts " index   Index into the ciphertext to start the check"
    $stderr.puts " prefix  The string to try at that prefix"
    $stderr.puts " ct1     Cipher text source (should be a binary file)"
    $stderr.puts " ct2     Cipher text destination (should be a binary file)"
    exit 42
end

usage unless ARGV.length >= 4
idx = ARGV.shift.to_i
prefix = ARGV.shift
c1 = File.new(ARGV.shift, "rb").read
c2 = File.new(ARGV.shift, "rb").read
lax = ARGV.size > 0

if idx + prefix.length > c1.length
    $stderr.puts "Index + prefix is too large for file"
    exit 1
end

key = []
# Generate the candidate random key starting at specified index
prefix.length.times do |i|
    key << (c1[idx + i] ^ prefix[i])
end

# Generate the candidate word from c2
ords = []
key.each_with_index do |k, i|
    ords << (c2[idx + i] ^ k)
end

candidate = ords.collect { |ord| ord.chr }.join
if valid_string candidate, lax
    puts "[%03d] [%s]  =>  [%s]" % [idx, prefix, candidate]
    # When we get the entire decoded message, output the key to stderr
    if key.length == c1.length
        $stderr.print key.collect { |ord| ord.chr }.join
    end
end
